package com.esdk.utils;

import com.esdk.esdk;
import com.esdk.sql.orm.IResultSetCursor;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.exceptions.JedisException;
import redis.clients.jedis.params.SetParams;

import java.io.Closeable;
import java.util.*;

public class RedisClient implements Closeable,AutoCloseable{
	private Jedis jedis;
	private int redisDB;

	public RedisClient(){
		redisDB=esdk.prop.getInteger("redis.database",0);
	}

	public RedisClient(int db) {
		redisDB=db;
	}

	public Jedis getRedis() {
		if(jedis==null||!jedis.isConnected()){
//			System.out.println("NumActive["+jedisPool.getNumActive()+"], NumIdle["+jedisPool.getNumIdle()+"], NumWaiters["+jedisPool.getNumWaiters()+"]");
			jedis=RedisUtils.getRedisPool().getResource();
			jedis.select(redisDB);
//			singleton=new Jedis(esdk.getConfig().getProperty("jedis_host"),Integer.valueOf(esdk.getConfig().getProperty("jedis_port")),5000);
		}
		return jedis;
	}

	public void close() {
		if(jedis!=null&&jedis.isConnected()) {
			try{
				jedis.close();
			}catch(JedisException e){
				e.printStackTrace();
			}finally {
				jedis=null;
			}
		}
	}

	public String[] keys(String pattern) {
		Set<String> set=getRedis().keys(pattern);
		String[] result=new String[set.size()];
		int i=0;
		for(String item:set) {
			result[i]=item;
			i++;
		}
		return result;
	}

	public Long del(String key) {
		return getRedis().del(key);
	}

	public Long unlink(String key) {
		return getRedis().unlink(key);
	}

	public Long ttl(String key) {
		return getRedis().ttl(key);
	}

	public Long del(String... keys) {
		if(keys!=null&&keys.length>0)
			return getRedis().del(keys);
		return 0L;
	}

	public Long unlink(String... keys) {
		if(keys!=null&&keys.length>0)
			return getRedis().unlink(keys);
		return 0L;
	}

	public Long delKeys(String pattern) {
		long result;
		Jedis jedis=getRedis();
		if(!pattern.endsWith("*"))
			pattern=pattern+"*";
		String[] keys=jedis.keys(pattern).toArray(new String[0]);
		if(keys.length>0) {
			result=jedis.del(keys);
		}
		else
			result=0L;
		return result;
	}

	public Long unlinkKeys(String pattern) {
		long result;
		Jedis jedis=getRedis();
		if(!pattern.endsWith("*"))
			pattern=pattern+"*";
		String[] keys=jedis.keys(pattern).toArray(new String[0]);
		if(keys.length>0) {
			result=jedis.unlink(keys);
		}
		else
			result=0L;
		return result;
	}
	/**
	 * 使用场景：点赞、签到、打卡
	 * 参考：https://mp.weixin.qq.com/s/so23ArKtMVZJHnP6ajF4Ag
	 * 将一个或多个成员元素加入到集合中，已经存在于集合的成员元素将被忽略。
	 * 假如集合 key 不存在，则创建一个只包含添加的元素作成员的集合。
	 * 当集合 key 不是集合类型时，返回一个错误。
	 * */
	public long sadd(String key,Object value) {
		return getRedis().sadd(key,esdk.str.valueOf(value));
	}
	/**
	 * 用于移除集合中的一个或多个成员元素，不存在的成员元素会被忽略。
	 * 当 key 不是集合类型，返回一个错误。
	 * */
	public long srem(String key,Object value) {
		return getRedis().srem(key,esdk.str.valueOf(value));
	}
	/**
	 * 判断成员元素是否是集合的成员。
	 * */
	public boolean sismember(String key,Object value) {
		return getRedis().sismember(key,esdk.str.valueOf(value));
	}

	/**返回集合中的所有的成员。 不存在的集合 key 被视为空集合*/
	public Set<String> smembers(String key) {
		Set<String> result=getRedis().smembers(key);
		return result;
	}

	/**返回集合中元素的数量*/
	public long scard(String key) {
		long result=getRedis().scard(key);
		return result;
	}

	/**
	 * 返回第一个集合与其他集合之间的差异，也可以认为说第一个集合中独有的元素。不存在的集合 key 将视为空集。
	 * 差集的结果来自前面的 FIRST_KEY ,而不是后面的 OTHER_KEY1，也不是整个 FIRST_KEY OTHER_KEY1..OTHER_KEYN 的差集。
	 * 实例:
	 * key1 = {a,b,c,d}
	 * key2 = {c}
	 * key3 = {a,c,e}
	 * SDIFF key1 key2 key3 = {b,d}
	 * */
	public Set<String> sdiff(String key) {
		Set<String> result=getRedis().sdiff(key);
		return result;
	}

	/**
	 * 返回给定所有给定集合的交集。 不存在的集合 key 被视为空集。 当给定集合当中有一个空集时，结果也为空集(根据集合运算定律)。
	 * */
	public Set<String> sinter(String key) {
		Set<String> result=getRedis().sinter(key);
		return result;
	}

	/**移出并获取列表的第一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止*/
	public List<String> blpop(int timeoutSec,String key){
		List<String> result=getRedis().blpop(timeoutSec,key);
		return result;
	}

	/**移出并获取列表的最后一个元素， 如果列表没有元素会阻塞列表直到等待超时或发现可弹出元素为止*/
	public List<String> brpop(int timeoutSec,String key){
		List<String> result=getRedis().brpop(timeoutSec,key);
		return result;
	}

	public void set(String key,Object value) {
		set(key,esdk.str.valueOf(value));
	}

	/**
	 * Redis Setnx（SET if Not eXists） 命令在指定的 key 不存在时，为 key 设置指定的值。
	 * @return 设置成功返回1，设置失败返回0
	 * */
	public void set(String key,String value) {
		getRedis().set(key,value);
	}
	public boolean setnx(String key,Object value) {
		long flag=getRedis().setnx(key,String.valueOf(value));
		return flag==1;
	}

	/**
	 * Redis Setnx（SET if Not eXists） 命令在指定的 key 不存在时，为 key 设置指定的值。
	 * @param sec 设置过期秒数
	 * @return 设置成功返回1，设置失败返回0
	 * */
	public boolean setnx(String key,Object value,int sec) {
		Jedis jedis=getRedis();
		String flag=jedis.set(key,String.valueOf(value),SetParams.setParams().nx().ex(sec));
		return Objects.equals(flag,"OK");
		/*long flag=jedis.setnx(key,String.valueOf(value));
		if(flag==1)
			flag=jedis.expire(key,sec);
			return true;*/
	}

	/**有编码问题，强制用utf8格式保存*/
	public void set(String key,String value,int sec) {
		Jedis jedis=getRedis();
		jedis.set(key,value);
		jedis.expire(key,sec);
	}

	public void set(String key,Object value,int sec) {
		set(key,value+"",sec);
	}

	/**注意value不能有逗号*/
	public boolean contains(String key,Object value){
		HashSet set=esdk.str.splitToSet(get(key));
		return set.contains(value+"");
	}

	public boolean exists(String key) {
		Jedis jedis=getRedis();
		return jedis.exists(key);
	}

	/**如果 KEY 存在，返回 1，否则，返回 0。用于判断多个 KEY 时，返回存在的 KEY 的数目。*/
	public long exists(String... key) {
		return getRedis().exists(key);
	}

	public long incr(String key) {
		return getRedis().incr(key);
	}

	public long incr(String key,int sec) {
		Jedis jedis=getRedis();
		long result=jedis.incr(key);
		jedis.expire(key,sec);
		return result;
	}

	public long decr(String key) {
		return getRedis().decr(key);
	}

	public long decr(String key,int sec) {
		Jedis jedis=getRedis();
		long result=jedis.decr(key);
		jedis.expire(key,sec);
		return result;
	}

	public RedisClient setObj(String key,Object value,int sec) {
		serialize(key,value,sec);
		return this;
	}

	public RedisClient setObj(String key,Object value) {
		serialize(key,value);
		return this;
	}

	public Object getObj(String key) {
		return unserialize(key);
	}

	public Object getObj(String key,int sec) {
		return unserialize(key,sec);
	}

	private void serialize(String key,Object value) {
		if(value!=null&&value instanceof IResultSetCursor)
			((IResultSetCursor)value).size();
		getRedis().set(esdk.str.getUTF8Bytes(key),SerializeUtil.serialize(value));
	}

	private void serialize(String key,Object value,int sec) {
		if(value!=null&&value instanceof IResultSetCursor)
			((IResultSetCursor)value).size();
		getRedis().setex(esdk.str.getUTF8Bytes(key),sec,SerializeUtil.serialize(value));
//	SerializeUtil.unserialize(SerializeUtil.serialize(value));
	}

	private Object unserialize(String key) {
		return SerializeUtil.unserialize(getRedis().get(esdk.str.getUTF8Bytes(key)));
	}

	private Object unserialize(String key,int sec) {
		Object result=SerializeUtil.unserialize(getRedis().get(esdk.str.getUTF8Bytes(key)));
		if(result!=null)
			getRedis().expire(key,sec);
		return result;
	}

	public void expire(String key,int sec) {
		getRedis().expire(key,sec); //秒为单位
	}

	public String get(String key) {
		Jedis jedis=getRedis();
		return jedis.get(key);
	}

	/**取值并设置过期时间*/
	public String get(String key,int sec) {
		Jedis jedis=getRedis();
		if(jedis.exists(key))
			jedis.expire(key,sec);
		return jedis.get(key);
	}

	public RedisClient hmset(String key,String... keyValues) {
		HashMap map=new HashMap(keyValues.length/2);
		for(int i=0,n=keyValues.length/2;i<n;i++) {
			map.put(keyValues[i],keyValues[i+1]);
		}
		getRedis().hmset(key,map);
		return this;
	}

	/**
	 * 用于同时将多个 field-value (字段-值)对设置到哈希表中。
	 * 此命令会覆盖哈希表中已存在的字段。
	 * 如果哈希表不存在，会创建一个空哈希表，并执行 HMSET 操作。
	 * */
	public String hmset(String key,Map map) {
		String result=getRedis().hmset(key,map);
		return result;
	}

	/**
	 * 用于返回哈希表中，一个或多个给定字段的值。
	 * 如果指定的字段不存在于哈希表，那么返回一个 nil 值。
	 * */
	public List<String> hmget(String key,String... fields) {
		return getRedis().hmget(key,fields);
	}

	public Long hset(String key,String field,String value) {
		return getRedis().hset(key,field,value);
	}

	/**用于为哈希表中的字段赋值 。
	 如果哈希表不存在，一个新的哈希表被创建并进行 HSET 操作。
	 如果字段已经存在于哈希表中，旧值将被覆盖。*/
	public Long hset(String key,String field,String value,int sec) {
		Long result=getRedis().hset(key,field,value);
		getRedis().expire(key,sec);
		return result;
	}

	public Long hincrBy(String key, String field, long incrValue){
		return getRedis().hincrBy(key, field, incrValue);
	}

	public Double hincrByFloat(String key, String field, double incrValue){
		return getRedis().hincrByFloat(key, field, incrValue);
	}

	public boolean hexists(String key, String field) {
		return getRedis().hexists(key, field);
	}

	/**返回哈希表中指定字段的值。*/
	public String hget(String key,String field) {
		return getRedis().hget(key,field);
	}

	public Object hgetObj(String key,String field) {
		return SerializeUtil.unserialize(getRedis().hget(esdk.str.getUTF8Bytes(key),esdk.str.getUTF8Bytes(field)));
	}

	public Long hsetObj(String key,String field,Object value,int sec) {
		if(value!=null&&value instanceof IResultSetCursor)
			((IResultSetCursor)value).size();
		Long result=getRedis().hset(esdk.str.getUTF8Bytes(key),esdk.str.getUTF8Bytes(field),SerializeUtil.serialize(value));
		getRedis().expire(key,sec);
		return result;
	}

}
